﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Reflection;
using SmallDataBase.Attribute;
using SmallDataBase.DTO;

namespace SmallDataBase
{
    /// <summary>
    /// 反射辅助类
    /// </summary>
    public sealed class Reflection
    {
        //原对象
        private readonly List<ProertyClass> _proertyList;
        private readonly object _source;
        private readonly Type _sourceType;
        private MetaDTO _meta;
        private TableAttribute _tableAttr;

        public Reflection(object source)
        {
            _source = source;
            _sourceType = _source.GetType();
            _proertyList = new List<ProertyClass>();
        }

        public Reflection(Type sourceType)
        {
            _sourceType = sourceType;
            _proertyList = new List<ProertyClass>();
        }

        private static ColumnAttribute GetColumnAttribute(PropertyInfo item)
        {
            object[] columnAttr = item.GetCustomAttributes(typeof (ColumnAttribute), false);
            if (columnAttr.Length != 0)
            {
                return columnAttr[0] as ColumnAttribute;
            }
            return null;
        }

        private void Init()
        {
            object[] tableAttr = _sourceType.GetCustomAttributes(typeof (TableAttribute), false);
            if (tableAttr.Length != 0)
            {
                _tableAttr = tableAttr[0] as TableAttribute;
            }

            foreach (PropertyInfo item in _sourceType.GetProperties())
            {
                var protery = new ProertyClass {PropertyInfo = item, ColumnAttribute = GetColumnAttribute(item)};

                if (_source != null)
                {
                    protery.Value = item.GetValue(_source, null);
                }
                _proertyList.Add(protery);
            }
        }

        public MetaDTO GetMeta()
        {
            Init();
            _meta = new MetaDTO();

            if (null != _tableAttr)
            {
                _meta.DataBaseName = _tableAttr.DataBaseName;
                _meta.TableName = _tableAttr.TableName;
            }
            else
            {
                _meta.TableName = _sourceType.Name;
            }

            ProertyClass auto =
                (from bar in _proertyList
                 where bar.ColumnAttribute != null && bar.ColumnAttribute.IsAutoIncrement
                 select bar).SingleOrDefault();
            if (auto != null)
            {
                _meta.AutoIncrement = auto.GetFieldDTO();
            }

            _meta.PrimaryKey = from bar in _proertyList
                               where
                                   bar.ColumnAttribute != null && bar.ColumnAttribute.IsPrimaryKey &&
                                   bar.ColumnAttribute.IsAutoIncrement == false
                               select bar.GetFieldDTO();
            _meta.Columns = from bar in _proertyList
                            where bar.ColumnAttribute == null || bar.ColumnAttribute.IsPrimaryKey == false
                            select bar.GetFieldDTO();
            return _meta;
        }

        public void SetAutoIncrementIDValue(object value)
        {
            foreach (var item in _proertyList)
            {
                if (item.ColumnAttribute!=null && item.ColumnAttribute.IsAutoIncrement)
                {
                    var val = Convert.ChangeType(value, item.PropertyInfo.PropertyType);
                    item.PropertyInfo.SetValue(_source, val, null);
                }
            }
        }

        /// <summary>
        /// 将DataRow转换成实体对象
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="dbtype">数据库类型</param>
        /// <param name="row"></param>
        /// <returns></returns>
        public static T GetObject<T>(DataRow row,DataBaseType dbtype) where T : new()
        {
            var instance = Activator.CreateInstance<T>();
            Type type = instance.GetType();
            foreach (PropertyInfo prop in type.GetProperties())
            {
                ColumnAttribute attr = GetColumnAttribute(prop);
                if (prop.CanWrite)
                {
                    string name = attr == null ? prop.Name : attr.ColumnName;
                    prop.SetValue(instance, TypeChange.Convert(row[name], dbtype), null);
                }
            }
            return instance;
        }

        #region ProertyClass

        private class ProertyClass
        {
            public PropertyInfo PropertyInfo { get; set; }

            public ColumnAttribute ColumnAttribute { get; set; }

            public object Value { private get; set; }

            public FieldDTO GetFieldDTO()
            {
                var dto = new FieldDTO
                              {
                                  Name = ColumnAttribute == null ? PropertyInfo.Name : ColumnAttribute.ColumnName,
                                  Value = Value,
                                  FieldType = PropertyInfo.PropertyType,
                                  IsAuto = ColumnAttribute != null && ColumnAttribute.IsAutoIncrement,
                                  IsPK = ColumnAttribute != null && ColumnAttribute.IsPrimaryKey
                              };

                return dto;
            }
        }

        #endregion
    }
}